<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.4.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">

<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Noto Serif SC:300,300italic,400,400italic,700,700italic|Source Code Pro:300,300italic,400,400italic,700,700italic&display=swap&subset=latin,latin-ext">
<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"example.com","root":"/","scheme":"Pisces","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":true,"show_result":true,"style":"flat"},"back2top":{"enable":true,"sidebar":false,"scrollpercent":true},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}};
  </script>

  <meta name="description" content="We’ve all been there. There’s a new requirement and you need to add or remove a view at runtime, and also need to reposition adjacent views.">
<meta property="og:type" content="article">
<meta property="og:title" content="UIStackView Tutorial Introducing Stack Views">
<meta property="og:url" content="http://example.com/2018/12/21/Swift/StackView/index.html">
<meta property="og:site_name" content="HR&#39;s Blog">
<meta property="og:description" content="We’ve all been there. There’s a new requirement and you need to add or remove a view at runtime, and also need to reposition adjacent views.">
<meta property="og:locale" content="en_US">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/1-starterProject-1-252x500.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/2-starterProject-480x242.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/3-starterProject-480x242.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/initialDeviceView-480x234.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/viewAsMenuSmaller-480x139.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/storyboardColors-299x500.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/spotInfoButtons-480x165.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/showDaOutline.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/buttonsSelected-480x209.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/stackButton.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/stackConfirmed-480x74.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/buttonConstrainOld.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/buttonConstraintsWiped.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/noConstraints-369x320.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/stackSelect1-480x112.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/stackSelect2.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/pinButton.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/add4Constraints-225x320.jpg">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/Screen-Shot-2017-07-23-at-2.05.40-PM-480x62.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/equalSpacing.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/4-squashedButton-252x500.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/Capto_Capture-2017-07-16_06-03-40_PM.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/5-niceButton-252x500.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/21-alternate-solution-1_346x76.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/ratingSelect-2.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/ratingStacked.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/add3Constraints.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/spacing8.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/xcodeBug-650x84.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/refreshLayoutButton.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/starsBack.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/unembed.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/whyVisitSelected-650x73.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/whyVisitStacked-650x76.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/whyVisitConstraintDetail.jpg">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/whyVisitConstrained-650x66.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/hortical.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/alignFill.jpg">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/alignLeading.jpg">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/alignCenter.jpg">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/alignTrailing.jpg">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/alignFillRed.jpg">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/whatToSee-650x305.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/40-weather-stack-in-stack_640x92.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/41-weather-stack-in-stack-alignment-bottom_640x92.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/weatherStacked-650x104.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/43-weather-click-stack-button_640x92.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/44-weather-alignment-fill_640x92.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/45-select-just-the-weather-label_640x92.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/46-weather-in-horizontal-stack_640x92.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/47-vertical-and-leading_640x92.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/6-hideInPlainSight-255x500.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/49-drag-to-weather-label_380x94.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/hideButtonAlignment-399x320.jpg">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/7-hidden-392x320.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/fiveSelected-650x245.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/topStack-650x167.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/topStackConstrained-650x253.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/8-hideInPlainSite2-255x500.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/hideButtonAlignment2.jpg">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/57-drag-and-drop-to-reposition-section_639x130.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/refreshLayoutButton.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/60-hide-button-back-to-correct-position_640x130.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/sizeClasses-480x271.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/CompactHeight-480x183.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/sizeClasses2-1-480x320.png">
<meta property="og:image" content="http://www.caohr.com/ImagesFolder/15319940423928.png">
<meta property="article:published_time" content="2018-12-21T07:12:43.000Z">
<meta property="article:modified_time" content="2018-12-21T07:12:43.000Z">
<meta property="article:author" content="HR">
<meta property="article:tag" content="Ray Wenderlich">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://www.caohr.com/ImagesFolder/1-starterProject-1-252x500.png">

<link rel="canonical" href="http://example.com/2018/12/21/Swift/StackView/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'en'
  };
</script>

  <title>UIStackView Tutorial Introducing Stack Views | HR's Blog</title>
  
    <script async src="https://www.googletagmanager.com/gtag/js?id=UA-61905451-1"></script>
    <script>
      if (CONFIG.hostname === location.hostname) {
        window.dataLayer = window.dataLayer || [];
        function gtag(){dataLayer.push(arguments);}
        gtag('js', new Date());
        gtag('config', 'UA-61905451-1');
      }
    </script>






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="Toggle navigation bar">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">HR's Blog</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">Swimming 🏊 in the sea🌊of code!</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/" rel="section"><i class="fa fa-home fa-fw"></i>Home</a>

  </li>
        <li class="menu-item menu-item-about">

    <a href="/about/" rel="section"><i class="fa fa-user fa-fw"></i>About</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>Tags</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>Categories</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>Archives</a>

  </li>
  </ul>
</nav>




</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>
  <div class="reading-progress-bar"></div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="en">
    <link itemprop="mainEntityOfPage" href="http://example.com/2018/12/21/Swift/StackView/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="HR">
      <meta itemprop="description" content="Swimming 🏊 in the sea🌊of code!">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="HR's Blog">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          UIStackView Tutorial Introducing Stack Views
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">Posted on</span>

              <time title="Created: 2018-12-21 15:12:43" itemprop="dateCreated datePublished" datetime="2018-12-21T15:12:43+08:00">2018-12-21</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">In</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/Swift/" itemprop="url" rel="index"><span itemprop="name">Swift</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <p>We’ve all been there. There’s a new requirement and you need to add or remove a view at runtime, and also need to reposition adjacent views.</p>
<span id="more"></span>

<p>What approach do you take? Do you add outlets to constraints in your storyboard so that you can activate or deactivate certain ones? Or do you use a third party library? Or depending on the complexity maybe you do everything in code.</p>
<p>Maybe this time around your view hierarchy didn’t have to change at runtime, but you still had to figure out how to squeeze this one new view into your storyboard.</p>
<p>Did you ever end up just clearing all constraints and re-adding them from scratch because it was easier than performing painstaking <em>constraints-surgery</em>?</p>
<p>With the introduction of <code>UIStackView</code>, the above tasks become trivial. In this UIStackView tutorial, you’ll learn how stack views provide an easy way to lay out a series of views horizontally or vertically. You’ll learn how to get views to automagically adjust themselves to the available space by using properties such as alignment, distribution, and spacing. Enjoy :]</p>
<p><em>Note</em>: This <code>UIStackView</code> tutorial assumes basic familiarity with Auto Layout. If you’re new to Auto Layout check out the <a target="_blank" rel="noopener" href="https://videos.raywenderlich.com/courses/62-beginning-auto-layout/lessons/1">Beginning Auto Layout</a> video tutorial.</p>
<h2 id="Getting-started"><a href="#Getting-started" class="headerlink" title="Getting started"></a>Getting started</h2><p>In this <code>UIStackView</code> tutorial, you’ll work on an app called <em>Vacation Spots</em>. It’s a simple app that shows you a list of places to get away from it all.</p>
<p>But don’t pack your bags just yet, because there are a few issues you’ll fix by using stack views, and in a much simpler way than if you were using Auto Layout alone.</p>
<p>Start by downloading the <a target="_blank" rel="noopener" href="https://koenig-media.raywenderlich.com/uploads/2017/07/VacationSpots_Starter-2.zip">starter project</a> for this <code>UIStackView</code> tutorial. Open the project and run it on the <em>iPhone 7 Simul<a target="_blank" rel="noopener" href="http://www.caohr.com/ImagesFolderator">http://www.caohr.com/ImagesFolderator</a></em>. You’ll see a list of places you can go on vacation.</p>
<p><img src="http://www.caohr.com/ImagesFolder/1-starterProject-1-252x500.png"></p>
<p>Go to the info view for London by tapping on the <em>London</em> cell.</p>
<p>At first glance, the view may seem okay, but it has a few issues.</p>
<ol>
<li>Look at the row of buttons at the bottom of the view. They are currently positioned with a fixed amount of space between themselves, so they don’t adapt to the screen width. To see the problem in full glory, temporarily rotate the simulator to landscape orientation by pressing <em>Command-left arrow</em>.底部的按钮菜单没有均匀分布</li>
</ol>
<p><img src="http://www.caohr.com/ImagesFolder/2-starterProject-480x242.png" alt="uistackview tutorial"></p>
<ol>
<li>Tap on the <em>Hide</em> button next to <em>WEATHER</em>. It successfully hides the text, but it doesn’t reposition the section below it, leaving a block of blank space. 点击天气旁边的hide，隐藏后，有一大片白色区域。</li>
</ol>
<p><img src="http://www.caohr.com/ImagesFolder/3-starterProject-480x242.png" alt="uistackview tutorial"></p>
<ol>
<li>The ordering of the sections can be improved. It would be more logical if the <em>what to see</em> section was positioned right after the <em>why visit</em> section, instead of having the <em>weather</em> section in between them.</li>
<li>The bottom row of buttons is a bit too close to the bottom edge of the view in landscape mode. It would be better if you could decrease the spacing between the different sections – but only in landscape mode.底部按钮太靠近底部了，如果在<code>landscape</code>模式下留足够的间距</li>
</ol>
<p>Now that you have an idea of the improvements you’ll be making, it’s time to dive into the project.</p>
<p>Open <em>Main.storyboard</em>. The first time you do that, you’ll be asked to <em>Choose an initial device view</em>. This view has no effect at runtime — the view will resize for different devices — this just makes it a bit easier to work with the storyboard. Select the iPhone 7.</p>
<p><img src="http://www.caohr.com/ImagesFolder/initialDeviceView-480x234.png" alt="uistackview tutorial"></p>
<p>You can change the view any time by clicking the <em>View As: iPhone 7</em> link at the bottom left of the storyboard canvas.</p>
<p><img src="http://www.caohr.com/ImagesFolder/viewAsMenuSmaller-480x139.png" alt="uistackview tutorial"></p>
<p>Now take a close look at the <em>Spot Info View Controller</em> scene. Whoa! What’s with all the colors?</p>
<p><img src="http://www.caohr.com/ImagesFolder/storyboardColors-299x500.png" alt="uistackview tutorial"></p>
<p>These labels and buttons have various background colors set that will be cleared at runtime. In the storyboard, they’re simply visual aids to help show how changing various properties of a stack view will affect the frames of its embedded views.这些<code>labels</code>和<code>buttons</code>都有背景颜色，这些颜色在运行的时候将会清空。</p>
<p>You don’t need to do this now, but if at any point you’d actually like to see the background colors while running the app, you can temporarily comment out the following lines in <code>viewDidLoad()</code> inside <code>SpotInfoViewController</code>.</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Clear background colors from labels and buttons</span></span><br><span class="line"><span class="keyword">for</span> view <span class="keyword">in</span> backgroundColoredViews &#123;</span><br><span class="line">    view.backgroundColor <span class="operator">=</span> <span class="type">UIColor</span>.clear</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Also, any outlet-connected labels have placeholder text that’s set to the name of the outlet variable to which they are connected. This makes it a bit easier to tell which labels will have their text updated at runtime. For example, the label with text ** is connected to:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">@IBOutlet</span> <span class="keyword">weak</span> <span class="keyword">var</span> whyVisitLabel: <span class="type">UILabel</span>!</span><br></pre></td></tr></table></figure>
<p>Enough introduction, let’s get started!</p>
<h2 id="Your-first-stack-view"><a href="#Your-first-stack-view" class="headerlink" title="Your first stack view"></a>Your first stack view</h2><p>第一件事要做的事情就是修复底部按钮之间的间距。<br>The first thing you’ll fix by using a stack view is the spacing between the bottom row of buttons. A stack view can distribute its views along its axis in various ways, and one of the ways is with an equal amount of spacing between each view.</p>
<p>Fortunately, embedding existing views into a new stack view is not rocket science. First, select all of the buttons at the bottom of the <em>Spot Info View Controller</em> scene by <em>clicking</em> on one, then <em>Command-click</em>on the other two:</p>
<p><img src="http://www.caohr.com/ImagesFolder/spotInfoButtons-480x165.png" alt="uistackview tutorial"></p>
<p>If the outline view isn’t already open, go ahead and open it by using the <em>Show Document Outline</em> button at the bottom left of the storyboard canvas:</p>
<p><img src="http://www.caohr.com/ImagesFolder/showDaOutline.png" alt="uistackview tutorial"></p>
<p>Verify that all 3 buttons are selected by checking them in the outline view:</p>
<p><img src="http://www.caohr.com/ImagesFolder/buttonsSelected-480x209.png" alt="uistackview tutorial"></p>
<p>In case they aren’t all selected, you can also <em>Command-click</em> on each button in the outline view to select them.</p>
<p>Once selected, click on the new <em>Stack</em> button in the Auto Layout toolbar at the bottom right of the storyboard canvas:</p>
<p><img src="http://www.caohr.com/ImagesFolder/stackButton.png"></p>
<p>The buttons will become embedded in a new stack view:</p>
<p><img src="http://www.caohr.com/ImagesFolder/stackConfirmed-480x74.png" alt="uistackview tutorial"></p>
<p>The buttons are now flush with each other – you’ll that fix shortly.</p>
<p>While the stack view takes care of positioning the buttons, you still need to add Auto Layout constraints to position the stack view itself. 当<code>stack view</code> 掌管了底部的按钮，你仍然需要添加<code>Auto Layout</code>来控制<code>StackView</code>本身。</p>
<p>当你把<code>View</code>嵌入到<code>Stack View</code>里面以后，任何在<code>View</code>上的约束都会被清除。<br>When you embed a view in a stack view, any constraints to other views are removed. For example, prior to embedding the buttons in a stack view, the top of the <em>Submit Rating</em> button had a vertical spacing constraint connecting it to the bottom of the <em>Rating:</em> label:</p>
<p><img src="http://www.caohr.com/ImagesFolder/buttonConstrainOld.png" alt="uistackview tutorial"></p>
<p>Click on the <em>Submit Rating</em> button to see that it no longer has any constraints attached to it:</p>
<p><img src="http://www.caohr.com/ImagesFolder/buttonConstraintsWiped.png" alt="uistackview tutorial"></p>
<p>Another way to verify that the constraints are gone is by looking at the <em>Size inspector</em> (⌥⌘5):</p>
<p><img src="http://www.caohr.com/ImagesFolder/noConstraints-369x320.png" alt="uistackview tutorial"></p>
<p>In order to add constraints to position the stack view itself, you’ll first need to select it. Selecting a stack view in the storyboard can get tricky if its views completely fill the stack view.</p>
<p>One simple way is to select the stack view in the outline view:</p>
<p><img src="http://www.caohr.com/ImagesFolder/stackSelect1-480x112.png" alt="uistackview tutorial"></p>
<p>另外一种方法就是按住<code>shift</code>键和右键一起。<br>Another trick is to hold <em>Shift</em> and <em>Right-click</em> on any of the views in the stack view, or <em>Control-Shift-click</em> if you’re using a trackpad. You’ll get a context menu that shows the view hierarchy at the location you clicked, and you simply select the stack view by clicking on it in the menu.</p>
<p>For now, select the stack view using the <em>Shift-Right-click</em> method:</p>
<p><img src="http://www.caohr.com/ImagesFolder/stackSelect2.png" alt="uistackview tutorial"></p>
<p>Now, click the <em>Pin</em> button on the Auto Layout toolbar to add constraints to it:</p>
<p><img src="http://www.caohr.com/ImagesFolder/pinButton.png" alt="uistackview tutorial"></p>
<p>First add a check to <em>Constrain to margins</em>. Then add the following constraints to the edges of your stack view:</p>
<p>Top: 20, Leading: 0, Trailing: 0, Bottom: 0</p>
<p>Double-check the numbers for the top, leading, trailing, and bottom constraints and make sure that the <em>I-beams</em> are selected. Then click on <em>Add 4 Constraints</em>:</p>
<p><img src="http://www.caohr.com/ImagesFolder/add4Constraints-225x320.jpg" alt="uistackview tutorial"></p>
<p>Now the stack view is the correct size, but it has stretched the first button to fill in any extra space:<br><img src="http://www.caohr.com/ImagesFolder/Screen-Shot-2017-07-23-at-2.05.40-PM-480x62.png" alt="uistackview tutorial"></p>
<p>The property that determines how a stack view lays out its views along its axis is its <code>distribution</code> property. Currently, it’s set to <code>Fill</code>, which means the contained views will completely fill the stack view along its axis. To accomplish this, the stack view will only expand one of its views to fill that extra space; specifically, it expands the view with the lowest horizontal content hugging priority, or if all of the priorities are equal, it expands the first view.</p>
<p>However, you’re not looking for the buttons to fill the stack view completely – you want them to be equally spaced.</p>
<p>Make sure the stack view is still selected, and go to the <em>Attributes inspector</em>. Change the <em>Distribution</em>from <em>Fill</em> to <em>Equal Spacing</em>:<br>因为之前是Fill所有之间不留空隙，将<code>Distribution</code>改为<code>Equal Spaceing</code></p>
<p><img src="http://www.caohr.com/ImagesFolder/equalSpacing.png" alt="uistackview tutorial"></p>
<p>Now build and run, tap on any cell, and rotate the simulator (⌘→). You’ll see that the bottom buttons now space themselves equally! </p>
<p>但是如果你用SE的模拟器，你会发现<code>Submit Rating</code>挤到了一起。<br>But there’s one more thing to consider. Build and run your app using the smaller <em>iPhone SE</em> simulator. Tap through to London and you’ll notice the <em>Submit Rating</em> is squashed.<br><img src="http://www.caohr.com/ImagesFolder/4-squashedButton-252x500.png" alt="uistackview tutorial"></p>
<p>Fortunately, that’s an easy fix. Go back to the <em>Attributes inspector</em>. Change the <em>Distribution</em> from <em>Equal Spacing</em> to <em>Fill Proportionally</em> and add 10 for spacing.<br>幸运的是这个很容易修复，返回<code>Attributes inspector</code>改变<code>Distribution</code>从<code>Equal Spacing</code>到<code>Fill Proportionally</code>然后添加10的<code>spacing</code></p>
<p><img src="http://www.caohr.com/ImagesFolder/Capto_Capture-2017-07-16_06-03-40_PM.png" alt="uistackview tutorial"></p>
<p>Now build and run one more time on the iPhone SE simulator and everything should look in order.</p>
<p><img src="http://www.caohr.com/ImagesFolder/5-niceButton-252x500.png" alt="uistackview tutorial"></p>
<p>Congratulations, you’ve built your first stack view!</p>
<p>In order to solve this problem without a stack view, you would have had to use spacer views, one between each pair of buttons. You’d have to add equal width constraints to all of the spacer views as well as lots of additional constraints to position the spacer views correctly.<br>如果没有<code>StackView</code>为了解决这个问题，你可能需要在他们之间放置等宽的<code>spacer views</code></p>
<p>It would have looked something like the following. For visibility in the screenshot, the spacer views have been given a light gray background:</p>
<p><img src="http://www.caohr.com/ImagesFolder/21-alternate-solution-1_346x76.png" alt="uistackview tutorial"></p>
<p>This isn’t too much of an issue if you only have to do this once in the storyboard, but many views are dynamic. It’s not a straightforward task to add a new button or to hide or remove an existing button at runtime because of the adjacent spacer views and constraints.</p>
<p>In order to hide a view within a stack view, all you have to do is set the contained view’s <code>hidden</code> property to <code>true</code> and the stack view handles the rest. This is how you’ll fix the spacing under the <em>WEATHER</em> label when the user hides the text below it. You’ll do that a bit later in this <code>UIStackView</code> tutorial once you’ve added the weather section labels into a stack view.<br>在<code>Stack View</code>中如果你需要隐藏一个view那么你只需要把这个view设置为<code>hidden</code>。剩下的就交给<code>Stack view</code>就可以了。</p>
<blockquote>
<p>目前你知道了怎样指定<code>view</code>之间的<code>spacing</code>，但是如果你要在特定的<code>view</code>后面留特定距离的间距，那么就会用到一个新的方法<code>setCustomSpacing:afterView</code><br><em>Note</em>: You now know how to specify the spacing between subviews in your stack. But what if you want different spacing after a specific subview? In iOS 11, you can do that with a new method, <a target="_blank" rel="noopener" href="https://developer.apple.com/documentation/uikit/uistackview/2866023-setcustomspacing"><em>setCustomSpacing:afterView</em></a>.</p>
</blockquote>
<h2 id="Converting-the-sections"><a href="#Converting-the-sections" class="headerlink" title="Converting the sections"></a>Converting the sections</h2><p>You will convert all of the other sections in <code>SpotInfoViewController</code> to use stack views as well. This will enable you to easily complete the remaining tasks. You’ll convert the rating section next.</p>
<h3 id="Rating-section"><a href="#Rating-section" class="headerlink" title="Rating section"></a>Rating section</h3><p>Right above the stack view that you just created, select the <em>RATING</em> label and the stars label next to it:</p>
<p><img src="http://www.caohr.com/ImagesFolder/ratingSelect-2.png" alt="uistackview tutorial"></p>
<p>Then click on the <em>Stack</em> button to embed them in a stack view:</p>
<p><img src="http://www.caohr.com/ImagesFolder/ratingStacked.png" alt="uistackview tutorial"></p>
<p>Normally, your next step would be to add your stack view constraints. But there seems to be a bug in Xcode 9 beta 4 that makes it necessary to give your stack view a width before adding relative constraints. So click on the <em>Pin</em> button, give your stack view a width of 200 and click the “Add 1 Constraint” button.</p>
<p>Now click on the <em>Pin</em> button once again. Place a checkmark in <em>Constrain to margins</em> and add the following <em>three</em> constraints:</p>
<p>Top: 20, Leading: 0, Bottom: 20</p>
<p><img src="http://www.caohr.com/ImagesFolder/add3Constraints.png" alt="uistackview tutorial"></p>
<p>Now go to the <em>Attributes inspector</em> and set the spacing to <em>8</em>:</p>
<p><img src="http://www.caohr.com/ImagesFolder/spacing8.png"></p>
<p>It’s possible you may see a <em>Misplaced Views</em> warning and see something like this in which the stars label has stretched beyond the bounds of the view:</p>
<p><img src="http://www.caohr.com/ImagesFolder/xcodeBug-650x84.png" alt="uistackview tutorial"></p>
<p>Sometimes Xcode may temporarily show a warning or position the stack view incorrectly, but the warning will disappear as you make other updates. You can usually safely ignore these.</p>
<p>However, to fix it immediately, click the <em>Refresh Layout</em> button right next to the stack view button.</p>
<p><img src="http://www.caohr.com/ImagesFolder/refreshLayoutButton.png"></p>
<p><img src="http://www.caohr.com/ImagesFolder/starsBack.png"></p>
<p>Build and run to verify that everything looks exactly the same as before.</p>
<h3 id="Unembedding-a-stack-view"><a href="#Unembedding-a-stack-view" class="headerlink" title="Unembedding a stack view"></a>Unembedding a stack view</h3><p>Before you go too far, it’s good to have some basic “first aid” training. Sometimes you may find yourself with an extra stack view that you no longer need, perhaps because of experimentation, refactoring or just by accident.</p>
<p>Fortunately, there is an easy way to <em>unembed</em> views from a stack view.</p>
<p>First, you’d select the stack view you want to remove. Then hold down the <em>Option</em> key and click on the <em>Stack</em> button. The click <em>Unembed</em> on the context menu that appears:</p>
<p><img src="http://www.caohr.com/ImagesFolder/unembed.png"></p>
<p>Another way to do this is to select the stack view and then choose *Editor <em><em>*</em> Unembed</em> from the menu.</p>
<h3 id="Your-first-vertical-stack-view"><a href="#Your-first-vertical-stack-view" class="headerlink" title="Your first vertical stack view"></a>Your first vertical stack view</h3><p>Now, you’ll create your first vertical stack view. Select the <em>WHY VISIT</em> label and the ** below it:</p>
<p><img src="http://www.caohr.com/ImagesFolder/whyVisitSelected-650x73.png" alt="uistackview tutorial"></p>
<p>Xcode will correctly infer that this should be a vertical stack view based on the position of the labels. Click the <em>Stack</em> button to embed both of these in a stack view:</p>
<p><img src="http://www.caohr.com/ImagesFolder/whyVisitStacked-650x76.png" alt="uistackview tutorial"></p>
<p>The lower label previously had a constraint pinning it to the right margin of the view, but that constraint was removed when it was embedded in the stack view. Currently, the stack view has no constraints, so it adopts the intrinsic width of its largest view.</p>
<p>With the stack view selected, click on the <em>Pin</em> button. Checkmark <em>Constrain to margins</em>, and set the <em>Top</em>, <em>Leading</em> and <em>Trailing</em> constraints to <em>0</em>.</p>
<p>Then, click on the dropdown to the right of the bottom constraint and select <em>WEATHER (current distance = 20)</em>:</p>
<p><img src="http://www.caohr.com/ImagesFolder/whyVisitConstraintDetail.jpg" alt="uistackview tutorial"></p>
<p>By default, constraints are shown to the nearest neighbor, which for the bottom constraint is the <em>Hide</em>button at a distance of 15. You actually needed the constraint to be to the <em>WEATHER</em> label below it.</p>
<p>Finally, click <em>Add 4 Constraints</em>. You should now see the following:</p>
<p><img src="http://www.caohr.com/ImagesFolder/whyVisitConstrained-650x66.png" alt="uistackview tutorial"></p>
<p>You now have an expanded stack view with its right edges pinned to the right margin of the view. However, the bottom label is still the same width. You’ll fix this by updating the stack view’s <code>alignment</code> property.</p>
<h3 id="Alignment-property"><a href="#Alignment-property" class="headerlink" title="Alignment property"></a>Alignment property</h3><p>The <code>alignment</code> property determines how a stack view lays out its views <em>perpendicular</em> to its axis. For a vertical stack view, the possible values are <code>Fill</code>, <code>Leading</code>, <code>Center</code>, and <code>Trailing</code>.</p>
<p>The possible <code>alignment</code> values for a <em>horizontal</em> stack view differ slightly:</p>
<p><img src="http://www.caohr.com/ImagesFolder/hortical.png" alt="uistackview tutorial"></p>
<p>It has <code>.top</code> instead of <code>.leading</code> and has <code>.bottom</code> instead of <code>.trailing</code>. There are also two more properties that are valid only in the horizontal direction, <code>.firstBaseline</code> and <code>.lastBaseline</code>.</p>
<p>Select each value to see how it affects the placement of the labels for the vertical stack view:</p>
<p><em>Fill:</em></p>
<p><img src="http://www.caohr.com/ImagesFolder/alignFill.jpg" alt="uistackview tutorial"></p>
<p><em>Leading:</em></p>
<p><img src="http://www.caohr.com/ImagesFolder/alignLeading.jpg" alt="uistackview tutorial"></p>
<p><em>Center:</em><br><img src="http://www.caohr.com/ImagesFolder/alignCenter.jpg" alt="uistackview tutorial"></p>
<p><em>Trailing:</em></p>
<p><img src="http://www.caohr.com/ImagesFolder/alignTrailing.jpg" alt="uistackview tutorial"></p>
<p>When you’re done testing each value, set the <em>Alignment</em> to <em>Fill</em>:</p>
<p><img src="http://www.caohr.com/ImagesFolder/alignFillRed.jpg" alt="uistackview tutorial"></p>
<p>Then build and run to verify that everything looks good and that there are no regressions.</p>
<p><code>Fill</code>具体说明是所有的<code>View</code>完全填充<code>stackview</code><br>Specifying <code>Fill</code> means you want all the views to completely fill the stack view perpendicular to its axis. This causes the <em>WHY VISIT</em> label to expand itself to the right edge as well.</p>
<p>But what if you only wanted the bottom label to expand itself to the edge?<br>但是如果你只是希望底部的bottom label扩展自己的边缘呢？</p>
<p>For now, it doesn’t matter since both labels will have a clear background at runtime, but it will matter when you’re converting the weather section.</p>
<p>You’ll learn how to accomplish that with the use of an additional stack view.</p>
<h3 id="Convert-the-“what-to-see”-section"><a href="#Convert-the-“what-to-see”-section" class="headerlink" title="Convert the “what to see” section"></a>Convert the “what to see” section</h3><p>This section is very similar to the previous one, so the instructions here are brief.</p>
<ol>
<li>First, select the <em>WHAT TO SEE</em> label and the ** below it.</li>
<li>Click on the <em>Stack</em> button.</li>
<li>Click on the <em>Pin</em> button.</li>
<li>Checkmark <em>Constrain to margins</em>, and add the following <em>four</em> constraints:</li>
</ol>
<p>Top: 20, Leading: 0, Trailing: 0, Bottom: 20</p>
<ol>
<li>Set the stack view’s <em>Alignment</em> to <em>Fill</em>.</li>
</ol>
<p>Your storyboard should now look like this:</p>
<p><img src="http://www.caohr.com/ImagesFolder/whatToSee-650x305.png" alt="uistackview tutorial"></p>
<p>Build and run to verify that everything still looks the same.</p>
<p>This leaves you with just the <em>weather</em> section left.</p>
<h2 id="Convert-the-weather-section"><a href="#Convert-the-weather-section" class="headerlink" title="Convert the weather section"></a>Convert the weather section</h2><p>The <em>weather</em> section is more complex than the others due to the inclusion of the <em>Hide</em> button.</p>
<p>One approach you could take would be to create a nested stack view by embedding the <em>WEATHER</em> label and the <em>Hide</em> button into a horizontal stack view, and then embedding that horizontal stack view and the ** into a vertical stack view.</p>
<p>It would look something like this:</p>
<p><img src="http://www.caohr.com/ImagesFolder/40-weather-stack-in-stack_640x92.png" alt="uistackview tutorial"></p>
<p>一开始我们可能想到的办法是先把<code>weather</code>和<code>hide</code>按钮组合成一个<code>horizontal</code>的<code>stack view</code>，然后再将这个<code>Stack View</code>和<code>weatherinfoLabel</code>组合成一个<code>Stack View</code>，但是这就有个问题就是这两个<code>view</code>必须是等宽的。这种方案就无法满足<code>WeatherInfoLable</code>的自适应。<br>Notice that the <em>WEATHER</em> label has expanded to be equal to the height of the <em>Hide</em> button. This isn’t ideal since this will cause there to be extra space between the baseline of the <em>WEATHER</em> label and the text below it.</p>
<p>Remember that <code>alignment</code> specifies positioning perpendicular to the stack view. So, you could set the <code>alignment</code> to <em>Bottom</em>:</p>
<p><img src="http://www.caohr.com/ImagesFolder/41-weather-stack-in-stack-alignment-bottom_640x92.png" alt="uistackview tutorial"></p>
<p>But you really don’t want the height of the <em>Hide</em> button to dictate the height of the stack view.</p>
<p>The actual approach you’ll take is to have the <em>Hide</em> button <em>not</em> be in the stack view for the <em>weather</em> section, or any other stack view for that matter.</p>
<p>It will remain a subview of the top-level view, and you’ll add a constraint from it to the <em>WEATHER</em> label — which will be in a stack view. That’s right, you’ll add a constraint from a button outside of a stack view to a label within a stack view!</p>
<p>Select the <em>WEATHER</em> label and the <em><weatherInfoLabel></em> below it:</p>
<p><img src="http://www.caohr.com/ImagesFolder/weatherStacked-650x104.png" alt="uistackview tutorial"></p>
<p>Click on the <em>Stack</em> button:</p>
<p><img src="http://www.caohr.com/ImagesFolder/43-weather-click-stack-button_640x92.png" alt="uistackview tutorial"></p>
<p>Click on the <em>Pin</em> button, checkmark <em>Constrain to margins</em> and add the following <em>four</em> constraints:</p>
<p>Top: 20, Leading: 0, Trailing: 0, Bottom: 20</p>
<p>Set the stack view’s <em>Alignment</em> to <em>Fill</em>:</p>
<p><img src="http://www.caohr.com/ImagesFolder/44-weather-alignment-fill_640x92.png" alt="uistackview tutorial"></p>
<p>You need a constraint between the <em>Hide</em> button’s left edge and the <em>WEATHER</em> label’s right edge, so having the <em>WEATHER</em> label fill the stack view won’t work.</p>
<p>However, you <em>do</em> want the bottom ** to fill the stack view.</p>
<p>You can accomplish this by embedding just the <em>WEATHER</em> label into a vertical stack view. Remember that the <code>alignment</code> of a vertical stack view can be set to <code>.leading</code>, and if the stack view is stretched beyond its intrinsic width, its contained views will remain aligned to its leading side.</p>
<p>Select the <em>WEATHER</em> label using the document outline, or by using the <em>Control-Shift-click</em> method:</p>
<p><img src="http://www.caohr.com/ImagesFolder/45-select-just-the-weather-label_640x92.png" alt="uistackview tutorial"></p>
<p>Then click on the <em>Stack</em> button:</p>
<p><img src="http://www.caohr.com/ImagesFolder/46-weather-in-horizontal-stack_640x92.png" alt="uistackview tutorial"></p>
<p>Set <em>Alignment</em> to <em>Leading</em>, and make sure <em>Axis</em> is set to <em>Vertical</em>:</p>
<p><img src="http://www.caohr.com/ImagesFolder/47-vertical-and-leading_640x92.png"></p>
<p>Perfect! You’ve got the outer stack view stretching the inner stack view to fill the width, but the inner stack view allows the label to keep its original width! 通过将<code>weather</code>设置为单独的<code>stackView</code>，并将对齐模式设置为leading，这样来达到自适应。</p>
<p>Build and run. Why on earth is the <em>Hide</em> button hanging out in the middle of the text?</p>
<p><img src="http://www.caohr.com/ImagesFolder/6-hideInPlainSight-255x500.png" alt="uistackview tutorial"></p>
<p>It’s because when you embedded the <em>WEATHER</em> label in a stack view, any constraints between it and the <em>Hide</em> button were removed.</p>
<p>To add new constraints <em>Control-drag</em> from the <em>Hide</em> button to the <em>WEATHER</em> label:</p>
<p><img src="http://www.caohr.com/ImagesFolder/49-drag-to-weather-label_380x94.png" alt="uistackview tutorial"></p>
<p>Hold down <em>Shift</em> to select multiple options, and select <em>Horizontal Spacing</em> and <em>Baseline</em>. Then click on <em>Add Constraints</em>:</p>
<p><img src="http://www.caohr.com/ImagesFolder/hideButtonAlignment-399x320.jpg" alt="uistackview tutorial"></p>
<p>Build and run. The <em>Hide</em> button should now be positioned correctly, and since the label that is being set to hidden is embedded in a stack view, pressing <em>Hide</em> hides the label, and adjusts the views below it — all without having to manually adjust any constraints.</p>
<p><img src="http://www.caohr.com/ImagesFolder/7-hidden-392x320.png" alt="uistackview tutorial"></p>
<p>Now that all the sections are in unique stack views, you’re set to embed them all into an outer stack view, which will make the final two tasks trivial.</p>
<h3 id="Top-level-stack-view"><a href="#Top-level-stack-view" class="headerlink" title="Top-level stack view"></a>Top-level stack view</h3><p><em>Command-click</em> to select all five top-level stack views in the outline view:</p>
<p><img src="http://www.caohr.com/ImagesFolder/fiveSelected-650x245.png" alt="uistackview tutorial"></p>
<p>Then click on the <em>Stack</em> button:</p>
<p><img src="http://www.caohr.com/ImagesFolder/topStack-650x167.png" alt="uistackview tutorial"></p>
<p>Click the <em>Pin</em> button, checkmark <em>Constrain to margins</em> add constraints of <em>0</em> to all edges. Then set <em>Spacing</em>to <em>20</em> and <em>Alignment</em> to <em>Fill</em>. Your storyboard scene should now look like this:</p>
<p><img src="http://www.caohr.com/ImagesFolder/topStackConstrained-650x253.png" alt="uistackview tutorial"></p>
<p>Build and run:</p>
<p><img src="http://www.caohr.com/ImagesFolder/8-hideInPlainSite2-255x500.png" alt="uistackview tutorial"></p>
<p>Whoops! Looks like the hide button lost its constraints again when the <em>WEATHER</em> stack view was embedded in the outer stack view. No problem, just add constraints to it again in the same way you did before.</p>
<p><em>Control-drag</em> from the <em>Hide</em> button to the <em>WEATHER</em> label, hold down <em>Shift</em>, select both <em>Horizontal Spacing</em> and <em>Baseline</em>. Then click on <em>Add Constraints</em>:</p>
<p><img src="http://www.caohr.com/ImagesFolder/hideButtonAlignment2.jpg" alt="uistackview tutorial"></p>
<p>Build and run. The <em>Hide</em> button is now positioned correctly.</p>
<h3 id="Repositioning-views"><a href="#Repositioning-views" class="headerlink" title="Repositioning views"></a>Repositioning views</h3><p>Now that all of the sections are in a top-level stack view, you’ll modify the position of the <em>what to see</em>section so that it’s positioned above the <em>weather</em> section.</p>
<p>Select the <em>middle stack view</em> from the outline view and <em>drag it between</em> the first and second view.</p>
<p><em>Note:</em> Keep the pointer slightly to the left of the stack views that you’re dragging it between so that it remains a <em>subview</em> of the outer stack view. The little blue circle should be positioned at the left edge between the two stack views and not at the right edge:</p>
<p><img src="http://www.caohr.com/ImagesFolder/57-drag-and-drop-to-reposition-section_639x130.png" alt="uistackview tutorial"></p>
<p>And now the <em>weather</em> section is third from the top. If the <em>Hide</em> doesn’t move along with the <em>weather</em>section, click on the <em>Refresh Layout</em> button.</p>
<p><img src="http://www.caohr.com/ImagesFolder/refreshLayoutButton.png" alt="uistackview tutorial"></p>
<p>The <em>Hide</em> button will now be back in the correct position:</p>
<p><img src="http://www.caohr.com/ImagesFolder/60-hide-button-back-to-correct-position_640x130.png" alt="uistackview tutorial"></p>
<p>Granted, repositioning the view with Auto Layout and re-adding constraints would not have been the most difficult thing you’ve ever done, but didn’t this feel oh-so-much nicer?</p>
<h3 id="Size-class-based-configuration"><a href="#Size-class-based-configuration" class="headerlink" title="Size class based configuration"></a>Size class based configuration</h3><p>Finally, you can turn your attention to the one remaining task on your list. In landscape mode, vertical space is at a premium, so you want to bring the sections of the stack view closer together. To do this, you’ll use size classes to set the spacing of the top-level stack view to <em>10</em> instead of <em>20</em> when the vertical size class is compact.</p>
<p>Select the top-level stack view and click on the little <em>+</em> button next to <em>Spacing</em>:</p>
<p><img src="http://www.caohr.com/ImagesFolder/sizeClasses-480x271.png" alt="uistackview tutorial"></p>
<p>Choose <em>Any Width</em> &gt; <em>Compact Height</em> and select <em>Add Variation</em></p>
<p><img src="http://www.caohr.com/ImagesFolder/CompactHeight-480x183.png" alt="uistackview tutorial"></p>
<p>And set the <em>Spacing</em> to <em>10</em> in the new <em>wAny hC</em> field:</p>
<p><img src="http://www.caohr.com/ImagesFolder/sizeClasses2-1-480x320.png" alt="uistackview tutorial"></p>
<p>Build and run. The spacing in portrait mode should remain unchanged. Rotate the simulator (⌘←) and note that the spacing between the sections has decreased and the buttons now have ample space from the bottom of the view:</p>
<p><img src="http://www.caohr.com/ImagesFolder/15319940423928.png"></p>
<p>If you didn’t add a top-level stack view, you still <em>could</em> have used size classes to set the vertical spacing to 10 on each of the four constraints that separate the five sections, but isn’t it so much better to set it in just a single place?</p>
<p>You have better things to do with your time, like animation!</p>
<h2 id="Animation"><a href="#Animation" class="headerlink" title="Animation"></a>Animation</h2><p>Currently, it’s a bit jarring when hiding and showing the weather details. You’ll add some animation to smooth the transition.</p>
<p>Stack views are fully compatible with the <code>UIView</code> animation engine. This means that animating the appearance/disappearance of an arranged subview, is as simple as toggling its <code>hidden</code> property <em>inside</em> an animation block.</p>
<p>It’s time to write some code! Open <em>SpotInfoViewController.swift</em> and take a look at <code>updateWeatherInfoViews(hideWeatherInfo:animated:)</code>.</p>
<p>You’ll see this line at the end of the method:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">weatherInfoLabel.hidden <span class="operator">=</span> shouldHideWeatherInfo</span><br></pre></td></tr></table></figure>
<p>Replace it with the following:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> animated &#123;</span><br><span class="line">    <span class="type">UIView</span>.animate(withDuration: <span class="number">0.3</span>) &#123;</span><br><span class="line">        <span class="keyword">self</span>.weatherInfoLabel.isHidden <span class="operator">=</span> shouldHideWeatherInfo</span><br><span class="line">    &#125;</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    weatherInfoLabel.isHidden <span class="operator">=</span> shouldHideWeatherInfo</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>Build and run, and tap the <em>Hide</em> or <em>Show</em> button. Doesn’t the animated version feel much nicer?</p>
<p>In addition to animating the hidden property on views contained within the stack view, you can also animate properties on the stack view itself, such as <code>alignment</code>, <code>distribution</code>, <code>spacing</code> and even the <code>axis</code>.</p>
<h2 id="Where-to-go-from-here"><a href="#Where-to-go-from-here" class="headerlink" title="Where to go from here?"></a>Where to go from here?</h2><p>You can download the completed project <a target="_blank" rel="noopener" href="https://koenig-media.raywenderlich.com/uploads/2017/07/VacationSpots_Final-1.zip">here</a>.</p>
<p>In this <code>UIStackView</code> tutorial, you learned a lot about stack views as well as the various properties that a stack view uses to position its subviews. Stack views are highly configurable, and there may be more than one way achieve the same result.</p>
<p>The best way to build on what you’ve learned is to experiment with various properties yourself. Instead of setting a property and moving on, see how playing with the other properties affects the layout of views within the stack view.</p>
<p>In the meantime, if you have any questions or comments about this <code>UIStackView</code> tutorial or Stack Views in general, please join the forum discussion below!</p>

    </div>

    
    
    
        

  <div class="followme">
    <p>Welcome to my other publishing channels</p>

    <div class="social-list">

        <div class="social-item">
          <a target="_blank" class="social-link" href="/atom.xml">
            <span class="icon">
              <i class="fa fa-rss"></i>
            </span>

            <span class="label">RSS</span>
          </a>
        </div>
    </div>
  </div>


      <footer class="post-footer">
          <div class="post-tags">
              <a href="/tags/Ray-Wenderlich/" rel="tag"># Ray Wenderlich</a>
          </div>

        


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/2018/12/21/Swift/Mutating/" rel="prev" title="Mutating">
      <i class="fa fa-chevron-left"></i> Mutating
    </a></div>
      <div class="post-nav-item">
    <a href="/2018/12/21/Swift/Pattern%20Matching%20in%20Swift/" rel="next" title="Pattern Matching in Swift">
      Pattern Matching in Swift <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          Table of Contents
        </li>
        <li class="sidebar-nav-overview">
          Overview
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#Getting-started"><span class="nav-number">1.</span> <span class="nav-text">Getting started</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Your-first-stack-view"><span class="nav-number">2.</span> <span class="nav-text">Your first stack view</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Converting-the-sections"><span class="nav-number">3.</span> <span class="nav-text">Converting the sections</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Rating-section"><span class="nav-number">3.1.</span> <span class="nav-text">Rating section</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Unembedding-a-stack-view"><span class="nav-number">3.2.</span> <span class="nav-text">Unembedding a stack view</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Your-first-vertical-stack-view"><span class="nav-number">3.3.</span> <span class="nav-text">Your first vertical stack view</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Alignment-property"><span class="nav-number">3.4.</span> <span class="nav-text">Alignment property</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Convert-the-%E2%80%9Cwhat-to-see%E2%80%9D-section"><span class="nav-number">3.5.</span> <span class="nav-text">Convert the “what to see” section</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Convert-the-weather-section"><span class="nav-number">4.</span> <span class="nav-text">Convert the weather section</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Top-level-stack-view"><span class="nav-number">4.1.</span> <span class="nav-text">Top-level stack view</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Repositioning-views"><span class="nav-number">4.2.</span> <span class="nav-text">Repositioning views</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Size-class-based-configuration"><span class="nav-number">4.3.</span> <span class="nav-text">Size class based configuration</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Animation"><span class="nav-number">5.</span> <span class="nav-text">Animation</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Where-to-go-from-here"><span class="nav-number">6.</span> <span class="nav-text">Where to go from here?</span></a></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">HR</p>
  <div class="site-description" itemprop="description">Swimming 🏊 in the sea🌊of code!</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">38</span>
          <span class="site-state-item-name">posts</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">9</span>
        <span class="site-state-item-name">categories</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">6</span>
        <span class="site-state-item-name">tags</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="mailto:caohr@outlook.com" title="E-Mail → mailto:caohr@outlook.com" rel="noopener" target="_blank"><i class="fa fa-envelope fa-fw"></i>E-Mail</a>
      </span>
  </div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2021</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">HR</span>
</div>
  <div class="powered-by">Powered by <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://pisces.theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Pisces</a>
  </div>

        








      </div>
    </footer>
  </div>

  
  <script src="/lib/anime.min.js"></script>
  <script src="/lib/velocity/velocity.min.js"></script>
  <script src="/lib/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/pisces.js"></script>


<script src="/js/next-boot.js"></script>




  















  

  

</body>
</html>
